// 一、promise
class MyPromise {
    // executor(resolve, reject)
    constructor(executor) {
        this.onRejectedCbs = []
        this.onResolvedCbs = []
        this.status = "pending"
        this.data = null
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch (err) {
            this.reject(err)
        }
    }
    resolve(res) {
        if (this.status === "pending") {
            this.status = "fulfilled"
            this.data = res
            for (const fn of this.onResolvedCbs) {
                fn(this.data)
            }
        }
    }
    reject(res) {
        if (this.status === "pending") {
            this.status = "rejected"
            this.data = res
            for (const fn of this.onRejectedCbs) {
                fn(this.data)
            }
        }
    }
    then(onResolved, onRejected) {
        onResolved = typeof onResolved === "function" ? onResolved : res => res;
        onRejected = typeof onRejected === "function" ? onRejected : err => { throw err }
        const promise2 = new MyPromise((resolve, reject) => {
            if (this.status === "pending") {
                this.onResolvedCbs.push(value => {
                    setTimeout(() => {
                        try {
                            const x = onResolved(value)
                            this.resolvedPromise(x, promise2, resolve, reject)
                        } catch (err) { reject(err) }
                    })
                })
                this.onRejectedCbs.push((reason) => {
                    setTimeout(() => {
                        try {
                            const x = onRejected(reason)
                            this.resolvedPromise(x, promise2, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    })
                })(onRejected)
            }
            if (this.status === "fulfilled") {
                setTimeout(() => {
                    try {
                        for (const fn in this.onResolvedCbs) {
                            const x = fn(this.data)
                            resolvedPromise(x, promise2, resolve, reject)
                        }
                    } catch (err) {
                        reject(err)
                    }
                })
            }
            if (this.status === "rejected") {
                setTimeout(() => {
                    try {
                        for (const fn of this.onRejectedCbs) {
                            const x = fn(this.data)
                            onResolvePromise(x, promise2, resolve, reject)
                        }
                    } catch (err) {
                        reject(err)
                    }
                })
            }
        })
        return promise2
    }
    resolvedPromise(x, promise2, resolve, reject) {
        let called = false
        if (x === promise2) {
            return reject(new TypeError("Chaining"))
        }
        else if (x instanceof MyPromise) {
            if (x.status === "pending") {
                x.then((y) => {
                    this.resolvedPromise(y, promise2, resolve, reject)
                }, reject)
            } else {
                x.then(resolve, reject)
            }

        }
        else if (Object.prototype.toString.call(x) === "[object Object]" || Object.prototype.toString.call(x) === "[object Function]") {
            // 防止重复
            try {
                const then = x.then
                // 不是 Promise
                if (typeof then !== "function") {
                    if (called) return
                    called = true
                    resolve(x)
                }
                if (called) return
                x.then.call(x, (y) => {
                    if (called) return
                    called = true
                    this.resolvedPromise(y, promise2, resolve, reject)
                }, r => {
                    if (called) return
                    called = true
                    reject(r)
                })
            } catch (err) {
                if (called) return
                called = true
                reject(err)
            }
        }
        else {
            resolve(x)
        }
    }
    finally(fn) {
        return this.then((value) => {
            fn(value)
            return value
        }, reason => {
            fn(reason)
            throw reason
        })
    }
    catch(fn) {
        return this.then(null, fn)
    }
    done() {
        this.catch(reason => {
            throw reason
        })
    }
    all(promisList) {
        let res = [], i = 1;
        const count = promisList.length
        return new MyPromise((resolve, reject) => {
            for (const p in promisList) {
                p.then(value => {
                    res.push(value)
                    if (i === count) resolve(res)
                }, reject)
                i++;
            }
        })
    }
    race(promiseList) {
        return new MyPromise((resolve, reject) => {
            for (const p in promiseList) {
                p.then((value) => {
                    resolve(value)
                }, reject)
            }
        })
    }
}
// 二、lazyman
class lazyman {
    constructor(name) {
        this.task = []
        this.task.push(() => {
            return new Promise(resolve => {
                console.log("my name is " + name)
                resolve()
            })
        })
        // 添加task是同步操作，等全部任务添加到this.task后，再运行
        setTimeout(() => { this.run() }, 0)
    }
    run() {
        console.log("run")
        let queue = Promise.resolve()
        for (const fn of this.task) {
            queue = queue.then(() => fn())
        }
    }

    sleep(ms) {
        this.task.push(new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("sleep " + ms + "ms")
                resolve()
            }, ms)
        }))
        return this
    }
    eat(food) {
        this.task.push(() => new Promise((resolve, reject) => {
            console.log("eat " + food)
            resolve()
        }))
        return this
    }
    sleepFirst(ms) {
        this.task.unshift(() => new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("sleep first " + ms)
                resolve()
            }, ms)
        }))
        return this
    }
}
// new lazyman("lihua").sleep(1000).eat("yeye").sleepFirst(2000)

// 三、
function timeoutPromise(request, timeout) {
    function sleep(delay) {
        return new Promise((resolve, reject) => {
            setTimeout(() =>
                reject(new Error("timeout")), delay
            )
        })
    }
    const promises = [request, sleep(timeout)]
    return new Promise((resolve, reject) => {
        for (const p of promises) {
            p.then(res => resolve(res)).catch(err => reject(err))
        }
    })

}
function turntablePromise(requestFn, timeout, animationDuration) {
    function sleep(delay) {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve(delay)
            })
        })
    }
    return Promise.all([timeoutPromise(requestFn(), timeout), sleep(animationDuration)])
}
